package org.springframework.boot.autoconfigure.validation;

import org.springframework.stereotype.Component;
import org.springframework.validation.BindException;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.RequestBody;
import top.malaoshi.easy.logging.Log;
import top.malaoshi.easy.logging.LogFactory;
import top.malaoshi.sprout.validation.Validator;
import top.malaoshi.util.TypeUtil;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.lang.reflect.Method;
import java.util.Set;

public class ValidatorImpl implements Validator {
    static final Log LOG = LogFactory.getLog(ValidatorImpl.class);
    /**
     * 使用hibernate validator进行参数校验
     *
     * 触发校验方式：类名上加 @Validated 注解，或者方法名上加 @Validated 注解
     *
     * 形参是字符串类型
     * 校验失败情况：不符合校验规则，会抛出 ConstraintViolationException 异常
     *
     * -----------------------------------------------------
     *
     * 形参是 bean 类型
     *
     * 注意：形参是 bean 类型，前面加 @Valid 注解，否则不会触发校验
     *
     * 校验失败情况：
     * - 形参是 bean 类型，但前面 没有加 @RequestBody，导致无法给bean赋值，会抛出 BindException 异常
     * - 不符合 bean 中的校验规则，会抛出 MethodArgumentNotValidException 异常
     *
     * @param bean
     * @param method
     * @param args
     */
    public void validate(Object bean, Method method, Object[] args){
        // 是否进行校验
        Validated validated = method.getAnnotation(Validated.class);
        if(validated!=null){
        }else {
            validated = bean.getClass().getAnnotation(Validated.class);
        }
        if(validated!=null) {
            Set<ConstraintViolation<Object>> constraintViolations = HibernateValidatorUtil
                    .validateMethodReturn(bean, method, args,validated.value());

            if(constraintViolations == null || constraintViolations.isEmpty()){
                return;
            }
            Class<?>[] parameterTypes = method.getParameterTypes();
            // true：表示形参是 bean
            boolean isBean = false;
            // 没有@RequestBody 注解
            boolean notRequestBody = false;
            for(Class<?> parameterType : parameterTypes){
                boolean primitive = TypeUtil.isPrimitive(parameterType);
                // 如果不是基本数据类型、字符串，也就是bean
                if(!primitive && parameterType != String.class){
                    isBean = true;
                    // 如果没有 @RequestBody 注解
                    if(!parameterType.isAnnotationPresent(RequestBody.class)){
                        notRequestBody = false;
                        break;
                    }
                }
            }
            if(isBean && notRequestBody) {
                throw new BindException(constraintViolations);
            }else if(isBean && !notRequestBody){
                throw new MethodArgumentNotValidException(constraintViolations);
            }else if(!isBean){
                throw new ConstraintViolationException(constraintViolations);
            }else{
                LOG.error("无法进行校验");
            }
        }
    }
}
